-- Id: $Id: BagSync.toc 26541 2007-01-30 00:14:59Z kergoth $
-- Version: r$Revision: 26541 $

--[[--------------------------------------------------------------------------------
  BagSync

  Author:  Derkyle
  Website: http://www.manaflux.com
  
  Inspired by Tuller and Bagnon
-----------------------------------------------------------------------------------]]

BINDING_HEADER_BAGSYNC = "BagSync";
BINDING_NAME_BAGSYNC = "Toggle BagSync Frame";

BagSync = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceModuleCore-2.0", "AceHook-2.1", "AceDebug-2.0", "AceConsole-2.0", "AceDB-2.0")

function BagSync:OnInitialize()

	local defaults = {
		debug = false,
		show = true,
	}
	
	local args	   = {
		type="group", 
		args = {
			debug = {
				name = "Debug", type = 'toggle', order = 1,
				desc = "Turns display of debugging text on and off.",
				get = function() return self.db.profile.debug end,
				set = function(v) 
					self.db.profile.debug = v
					if (v) then self:SetDebugging(true); else self:SetDebugging(false); end
				end,
			},
		}
	}
	
	local AceDB = AceLibrary("AceDB-2.0") --get character info
	self.faction = AceDB.FACTION
	self.charId = AceDB.CHAR_ID
	self.realm = AceDB.REALM
	self.charname = AceDB.NAME

	self.gnome = AceLibrary("Metrognome-2.0")

	self:RegisterDB("BagSyncDB") --register the database
	self:RegisterDefaults('profile', defaults) --register default profile
	self:RegisterDefaults("account", { ['*'] = { ['*'] = { } } }) --register the account storage
	self:RegisterChatCommand({"/bagsync", "/bsync", "/bgs", "/bs"}, args) --register chat commands

end

function BagSync:OnEnable()

	--bindings lang setting
	BINDING_NAME_BAGSYNC = BagSyncL["ToggleMain"];

	--register events
	self:RegisterEvent("PLAYER_ENTERING_WORLD",	  function() self:EventResync(event) end)
	self:RegisterEvent("PLAYER_LEAVING_WORLD",	  function() self:EventResync(event) end)
	self:RegisterEvent("UNIT_INVENTORY_CHANGED",	  function() if arg1 == 'player' then self:ScanEquip(); end end)
	self:RegisterEvent("PLAYER_LOGIN",	  function()
		self:ScanEquip();
		self:ScanBag(0);
		if (GetKeyRingSize() > 0) then self:ScanBag(-2); end
		
		for bag = 0, 4, 1 do
			self:ScanBag(bag)
		end
	end)
	self:RegisterEvent("BAG_UPDATE",	  function() self:BagUpdate(arg1) end)
	self:RegisterEvent("ITEM_PUSH",	  function() self:BagUpdate(arg1) end)

	self:RegisterEvent("BANKFRAME_CLOSED",	  function() self:SetBank() end)
	self:RegisterEvent("BANKFRAME_OPENED",	  function() self:SetBank(1) end)
	
	--this is to scan first bank bag (When items change or are moved)
	self:RegisterEvent("PLAYERBANKSLOTS_CHANGED", function()  self:BagUpdate(-1) end)
	
	
	--set initial debugging
	if(self.db.profile.debug) then self:SetDebugging(true); else self:SetDebugging(false); end
	
	self:Print("Version [ |c00FF9933"..self.version.."|r ] has been loaded.") --print load version
end

function BagSync:OnDisable()
	--unregister stuff registered onenable
end

function BagSync:EventResync(event)

	self:Debug("EventResync: %s", event)
	
	if ( event == "PLAYER_LEAVING_WORLD" ) then
		self:UnregisterEvent("UNIT_INVENTORY_CHANGED")
		self:UnregisterEvent("BAG_UPDATE")
		self:UnregisterEvent("ITEM_PUSH")
		
	end
	if ( event == "PLAYER_ENTERING_WORLD" ) then
		self:RegisterEvent("UNIT_INVENTORY_CHANGED",	  function() if arg1 == 'player' then self:ScanEquip(); end end)
		self:RegisterEvent("BAG_UPDATE",	  function() self:BagUpdate(arg1) end)
		self:RegisterEvent("ITEM_PUSH",	  function() self:BagUpdate(arg1) end)
	end

end

function BagSync:_removeNegative(l)
	if (not l) then return nil; end
	--this function is used to remove the last portion of the itemid with the large numbers and replace it with zero
	
	--special thanks to Kaelten for some advice =)
	local sVar = string.match(l,"([-0-9]+:[-0-9]+:[-0-9]+:[-0-9]+:[-0-9]+:[-0-9]+:[-0-9]+:)");
	
	if (not sVar) then return nil; end
	
	sVar = sVar.."0"; --add a zero at the end since we stripped it
	return sVar;
end

function BagSync:SetBank(atBank)
	self.UserAtBank = atBank
	self:ScanBank();

	self:Debug("Bank Set")
end


function BagSync:ScanBank()

	self:ScanBag(-1)
	for bag = 5, 11, 1 do
		self:ScanBag(bag)
	end
	
	self:Debug("Bank Scan")
end


function BagSync:ScanEquip()

	-- -99 = equipment
	if (not self.db.account[self.realm][self.charname][-99]) then
		self.db.account[self.realm][self.charname][-99]  = {}
	end
	
	--first lets check if the only thing that changed was ammo.  If this was the case then we want
	--to ignore the scanning of equipment
	local ammo_link = GetInventoryItemLink('player', 0) --0 slot is the ammo slot
	local ammo_count = GetInventoryItemCount('player', 0) --0 slot is the ammo slot
	
	--if ammo count is different then they fired, save new count and ignore check
	if (ammo_link and tonumber(ammo_count) and tonumber(ammo_count) > 0) then
		if (ammo_link == self.ammo_link and ammo_count ~= self.ammo_count) then
			self.ammo_link = ammo_link;
			self.ammo_count = ammo_count;
			return nil;
		else
			self.ammo_link = ammo_link;
			self.ammo_count = ammo_count;
		end
	end
	
	self:Debug("Equipment Scan")
	
	for i = 0, 19 do
	
		local link = GetInventoryItemLink('player', i)
			
		if (link) then
			link = self:_removeNegative(link);
			local regid = string.gsub(link, "([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+)", "%1:%7")
			
			if (regid) then
				local count = GetInventoryItemCount('player', i)
				self.db.account[self.realm][self.charname][-99][i] = format('%s,%s', regid, count)
			end
		else
			self.db.account[self.realm][self.charname][-99][i] = nil
		end
	end
	
end

function BagSync:BagUpdate(bagid)
	if (bagid) then self:Debug("BagUpdate: "..bagid); end
	
	if(bagid) then
		if (not tonumber(bagid)) then return nil; end
		
		--[ContainerIDToInventoryID]
		--After TBC Closed Beta patch v2.0.3.6299 (8th Jan 2007), inputs outside the range of 1-11
		--(1-4 for bags, and 5-11 for bank) now throw "invalid container ID" errors.
		if (tonumber(bagid) > 0 and tonumber(bagid) < 12) then
		
			local invID = ContainerIDToInventoryID(tonumber(bagid));
			if (invID) then
				local link = GetInventoryItemLink("player", invID)
				
				if (link) then
					
					link = self:_removeNegative(link);
					
					local coreid = string.gsub(link, "([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+)", "%1")
					if (not tonumber(coreid)) then return nil; end
					
					local _,_,_,_,_,_,subtype = GetItemInfo(tonumber(coreid)) 
					if (subtype) then
						if (BagSyncA["BAGS"][subtype]) then --it's an ammo bag so lets ignore it
							if (bagid) then self:Debug("IsAmmoBag: "..bagid); end
							return nil;
						end
					end
				end
			end
		
		end
	end

	--check for bank first
	if (self.UserAtBank and self:CheckBankBag(bagid)) then
		self:Debug("Bank Scanned Processed For: ("..bagid..")")
		self:ScanBag(bagid);
		return nil;
	elseif (not self.UserAtBank and self:CheckBankBag(bagid)) then
		--if the user is not at bank and it's a bank bag then skip it
		return nil;
	end

	if (not self.gnome:Status("BagSync_BagUpdate"..bagid)) then

		local BagSync_BagUpdateDone = function(bagid)

			if (bagid) then
				self:ScanBag(bagid);
			end
			
			self:Debug("BagSync_BagUpdate("..bagid.."): ".. bagid)
			self.gnome:Unregister("BagSync_BagUpdate"..bagid);
		end
	
		self.gnome:Register("BagSync_BagUpdate"..bagid, BagSync_BagUpdateDone , 1, bagid)
		self:BagUpdate(bagid)
	else
		local avail, rate, running = self.gnome:Status("BagSync_BagUpdate"..bagid)

		if (not running ) then
			self.gnome:Start("BagSync_BagUpdate"..bagid, 1)
		else
			self.gnome:ChangeRate("BagSync_BagUpdate"..bagid, 1)
		end
	end
	
	
end

function BagSync:ScanBag(bagid)
	if (bagid) then self:Debug("BagCheck: "..bagid); end
	
	--check for bank bag
	if (self:CheckBankBag(bagid) and not self.UserAtBank) then return nil; end
	
	--INVENTORY_CONTAINER
	--BANK_CONTAINER
	--KEYRING_CONTAINER
	--PLAYER_CONTAINER
	--MAIL_CONTAINER

	local size
	
	if bagid == KEYRING_CONTAINER then
		size = GetKeyRingSize()
	else
		size = GetContainerNumSlots(bagid)
	end

	if size > 0 then
		if (not self.db.account[self.realm][self.charname][bagid]) then
			self.db.account[self.realm][self.charname][bagid]  = {}
		end
	
		for index = 1, size, 1 do
		
			local texture, count = GetContainerItemInfo(bagid, index)
			if( texture ) then
				local link = GetContainerItemLink(bagid, index)
				if (link) then
					
					link = self:_removeNegative(link);
					local regid = string.gsub(link, "([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+)", "%1:%7")
					if (regid) then
						self.db.account[self.realm][self.charname][bagid][index] = format('%s,%s', regid, count)
					end
				end
			
			else
				self.db.account[self.realm][self.charname][bagid][index] = nil; --empty it
			end
				
		end
		
	elseif (self.db.account[self.realm][self.charname][bagid]) then
		self.db.account[self.realm][self.charname][bagid] = nil
	end
	
end

function BagSync:CheckIvenBag(bagid)
	return bagid == KEYRING_CONTAINER or (bagid > -1 and bagid < 5)
end

function BagSync:CheckBankBag(bagid)
	return bagid == -1 or (bagid > 4 and bagid < 12)
end


--[[--------------------------------------------------------------------------------
BagSync Tooltip Display
Credit to Tuller for the Hooking Procedure (Bagnon)
-----------------------------------------------------------------------------------]]

function BagSync:AddTooltip(t, l)
	if (not t) then return nil; end
	
	l = self:_removeNegative(l);
	if (not l) then return nil; end
	
	local regid = string.gsub(l, "([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+):([-0-9]+)", "%1:%7")
	
	if (not regid) then return nil; end

	local bank, bags, equip = 0, 0, 0
	
	for k, v in pairs(self.db.account[self.realm]) do --grab users
		for kx, vx in pairs(self.db.account[self.realm][k]) do --user
			for kd, vd in pairs(self.db.account[self.realm][k][kx]) do --user
			
				if (vd) then
					local link, count = string.match(vd, '([%d%-:]+),(%d+)')
					
					if (link and count and link == regid) then
						
						if (self:CheckIvenBag(kx)) then
							bags = bags + count
						elseif (self:CheckBankBag(kx)) then
							bank = bank + count
						elseif (tonumber(kx) and tonumber(kx) == -99) then
							equip = equip + count
						end
					end
				end
			end
		end
		if ( bank + bags + equip > 0) then --don't process empty things duh
			
			local tString = "|c00CC99FF"..k..": ("
			
			if (bags > 0) then
				tString = tString.."Bags |c00CCFFFF"..bags.."|r|c00CC99FF"
			end
			if (bank > 0) then
				if (bags > 0) then
					tString = tString..", Bank |c00CCFFFF"..bank.."|r|c00CC99FF"
				else
					tString = tString.."Bank |c00CCFFFF"..bank.."|r|c00CC99FF"
				end
			end
			if (equip > 0) then
				if (bank > 0) then
					tString = tString..", Equiped |c00CCFFFF"..equip.."|r|c00CC99FF"
				else
					tString = tString.."Equiped |c00CCFFFF"..equip.."|r|c00CC99FF"
				end
			end

			tString = tString..")|r"
			
			t:AddLine(tString);
			self:Debug(bags.. " : ".. bank .. " : ".. equip);
		end
		
		--reset
		bank, bags, equip = 0, 0, 0
	end
	
		
end

function BagSync:HookGuard(action, method)
	return function(...)
		action(...)
		method(...)
	end
end

GameTooltip.SetBagItem = BagSync:HookGuard(GameTooltip.SetBagItem, function(self, bag, slot)
	BagSync:AddTooltip(self, GetContainerItemLink(bag, slot))
end)

GameTooltip.SetLootItem = BagSync:HookGuard(GameTooltip.SetLootItem, function(self, slot)
	BagSync:AddTooltip(self, GetLootSlotLink(slot))
end)

GameTooltip.SetHyperlink = BagSync:HookGuard(GameTooltip.SetHyperlink, function(self, link)
	BagSync:AddTooltip(self, link)
end)

GameTooltip.SetLootRollItem = BagSync:HookGuard(GameTooltip.SetLootRollItem, function(self, rollID)
	BagSync:AddTooltip(self, GetLootRollItemLink(rollID))
end)

GameTooltip.SetAuctionItem = BagSync:HookGuard(GameTooltip.SetAuctionItem, function(self, type, index)
	BagSync:AddTooltip(self, GetAuctionItemLink(type, index))
end)

SetItemRef = BagSync:HookGuard(SetItemRef, function(link, text, button)
	BagSync:AddTooltip(ItemRefTooltip, link)
end)

GameTooltip.SetTradeSkillItem = BagSync:HookGuard(GameTooltip.SetTradeSkillItem, function(self, skill, id)
	local link = (id) and GetTradeSkillReagentItemLink(skill, id) or GetTradeSkillItemLink(skill);
	BagSync:AddTooltip(self, link)
end)

GameTooltip.SetCraftItem = BagSync:HookGuard(GameTooltip.SetCraftItem, function(self, skill, id)
	local link = (id) and GetCraftReagentItemLink(skill, id) or GetCraftItemLink(skill);
	BagSync:AddTooltip(self, link)
end)

hooksecurefunc(GameTooltip,"SetInventoryItem",function(self, skill, id)
	local link = (skill) and GetInventoryItemLink(skill, id) or GetContainerItemLink(BANK_CONTAINER,this:GetID());
	BagSync:AddTooltip(self, link)
end);
